/*
* 所谓递归下降，就是每一种type的token，都对应着它自己的解析方式
* 跟antlr相对应就是，g4规定了文法，按什么样的方式去解析
* 解析到了tokens，遍历tokens
* 根据不同类型的token，来解析，解析完了之后返回
*
*
* 需要拆分：
*   按简单的步骤，一步步来做，才行
*   比如先解析简单语法
*       number boolean string null 都是直接返回的
*       keyword [break, const, continue, if, else, while, for, function, return, this, var]
*               关键词的返回必须要带类型，因为它有特定的操作
*       variable 也是直接返回
*
* */

const visitLiteral = (tokens, begin) => {
    let i = begin
    const token = tokens[i]
    const {tokenValue, tokenType} = token

    // 同理 string，number，boolean，null都是这样处理的

    // token是number的时候，可能是只有number，比如 var a = 10
    // 还有可能是数字之后跟了表达式，比如 var a = 99 + 1
    // 那能不能说是：「var a = 」这个模式后面是跟的表达式呢？
    // 这个比较合理: var a = expression
    // 也就是遇到 var a 之后，就去解析下一个expression
    // 那expression有很多种形式啊： a * b, a * function(10)...
    // 所以在拿到当前token之后，再拿一下next元素, 根据下一个元素来做决定
    log('number token:', tokenValue)
    if (i + 1 < tokens.length) {
        log(`${tokenValue}后有表达式式`)
        const nextToken = tokens[i + 1]
        if ([Type.plus, Type.minus, Type.multiply, Type.divide].includes(nextToken.tokenType)) {
            // 下一个元素是二元操作符，那么再下一个元素也是一个expression
            // + 号的后面是一个表达式，从i + 2 开始
            const r = ufmi(tokens, i + 2)

            // 移动到递归开始的位置。。其实应该要用 略过的符号 与 递归回来的移动距离
            // +2就是走到操作符, 再走到下一个表达式
            // r[1]就是递归消耗了几个, 之后往下走 靠 i += 1
            i += r[1] + 2
            log('右边的表达式:', r)
            return [
                {
                    name: 'binaryExpression',
                    operator: '+',
                    left: {
                        name: tokenType,
                        value: tokenValue,
                    },
                    right: r[0],
                },
                i - begin,
            ]
        } else {
            // 不是表达式。。再看吧
            // 不是2元表达式，则按一个数字处理
            log('只有一个数字：', tokenValue)
            return [
                {
                    name: tokenType,
                    value: tokenValue
                },
                i - begin + 1, // 消耗了几个token
            ]
        }
    } else {
        // 只有一个数字
        log('只有一个数字：', tokenValue)
        return [
            {
                name: tokenType,
                value: tokenValue
            },
            i - begin + 1, // 消耗了几个token
        ]
    }
}


const ufmi = (tokens, begin) => {
    let i = begin
    let body = []

    while (i < tokens.length) {
        const token = tokens[i]
        const {tokenValue, tokenType} = token
        log('当前访问', tokenValue, tokenType)
        if (tokenType === Type.keyword) {
            if (tokenValue === 'var') {
                // var a = 10
                const varName = tokens[i + 1]
                // 跳过 = 号, 从i + 3开始
                const v = ufmi(tokens, i + 3)
                log('var:', v)

                // 在这个示例中，ufmi的返回是number
                body.push({
                    name: 'var',
                    identifier: varName.tokenValue,
                    value: v[0]
                })
                log('body:', body)
                // 3是 [var, 变量名, =] 消耗了3个token
                // 消耗几个 是靠递归回来的 加上本次消耗的

                // 要消耗 [var, 变量名, =] 3个token，以及后面的表达式消耗
                i += v[1] + 2

                log('第一个var 后的i:', i)
            }
        } else if (tokenType === Type.number) {
            return visitLiteral(tokens, i)
        } else if (Type.curlyLeft === tokenType) {
            log('拿到左花括号了')
            // 【不要一上来就考虑所有情况，完成一个例子 再做下一个例子】
            /*
            比如{ value: 123 }
            遇到{ 之后，再处理key、colon、value
            每一对 key: value, 的处理都要返回一个{ key: xx, value: yy }
            * */
            const key = tokens[i + 1].tokenValue
            log('key:', key)
            const colon = tokens[i + 2]
            const value = ufmi(tokens, i + 3)
            let object = {
                key: key,
                value: value[0],
            }

            log('object:', object)
        }
        i += 1
        log('加1的body', body, i)
    }
    return [body, i - begin]
}

const test1 = () => {
    const code = `var a = 99`
    // const tl = new TokenList(code)
    // log(tl.tokenList)
    const tokenList = [
        {tokenValue: 'var', tokenType: Type.keyword},
        {tokenValue: 'a', tokenType: Type.variable},
        {tokenValue: '=', tokenType: Type.assign},
        {tokenValue: '99', tokenType: Type.number},
    ]
    const result = ufmi(tokenList, 0)
    log('result:', result)
}

const test2 = () => {
    const code = `var a = 99 + 1`
    const tokenList = [
        {tokenValue: 'var', tokenType: Type.keyword},
        {tokenValue: 'a', tokenType: Type.variable},
        {tokenValue: '=', tokenType: Type.assign},
        {tokenValue: 99, tokenType: Type.number},
        {tokenValue: '+', tokenType: Type.plus},
        {tokenValue: 1, tokenType: Type.number},

        // {tokenValue: 'var', tokenType: Type.keyword},
        // {tokenValue: 'b', tokenType: Type.variable},
        // {tokenValue: '=', tokenType: Type.assign},
        // {tokenValue: 'a', tokenType: Type.variable},
        // {tokenValue: '+', tokenType: Type.plus},
        // {tokenValue: 1, tokenType: Type.number},
    ]
    const result = ufmi(tokenList, 0)
    log('result:', result)
}

const test3 = () => {
    const code = `var a = "hello"`
    const tokenList = [
        {tokenValue: 'var', tokenType: Type.keyword},
        {tokenValue: 'a', tokenType: Type.variable},
        {tokenValue: '=', tokenType: Type.assign},
        {tokenValue: "hello", tokenType: Type.string},
    ]
    const result = ufmi(tokenList, 0)
    log('result:', result)
}

const test4 = () => {
    // 对象测试
    const code = `var a = {
        test: 123,
    }`
    const tokenList = [
        {tokenValue: 'var', tokenType: Type.keyword},
        {tokenValue: 'a', tokenType: Type.variable},
        {tokenValue: '=', tokenType: Type.assign},
        {tokenValue: "{", tokenType: Type.curlyLeft},
        {tokenValue: "test", tokenType: Type.variable},
        {tokenValue: ":", tokenType: Type.colon},
        {tokenValue: 123, tokenType: Type.number},
        {tokenValue: "}", tokenType: Type.curlyRight},
    ]
    /*
    预期的结果是:
    {
        identifier: 'a',
        name: 'var',
        value: {
            name: 'object',
            objectProperties: [
                {
                    key: "value",
                    value: 123,
                },
                {
                    key: "test",
                    value: 99,
                }
            ]
        }
    }
    * */

    const result = ufmi(tokenList, 0)
    log('result:', result)
}

const test5 = () => {
    const code = `var a = 1 + 2 + 3`
    const tokenList = [
        {tokenValue: 'var', tokenType: Type.keyword},
        {tokenValue: 'a', tokenType: Type.variable},
        {tokenValue: '=', tokenType: Type.assign},
        {tokenValue: 1, tokenType: Type.number},
        {tokenValue: "+", tokenType: Type.plus},
        {tokenValue: 2, tokenType: Type.number},
        {tokenValue: "+", tokenType: Type.plus},
        {tokenValue: 3, tokenType: Type.number},
    ]

    const result = ufmi(tokenList, 0)
    log('result:', result)
}

const test6 = () => {
    const code = `var o = {
        test: 123,
    }`
    const tokenList = [
        {tokenValue: 'var', tokenType: Type.keyword},
        {tokenValue: 'o', tokenType: Type.variable},
        {tokenValue: '=', tokenType: Type.assign},
        {tokenValue: '{', tokenType: Type.curlyLeft},
        {tokenValue: "test", tokenType: Type.variable},
        {tokenValue: ':', tokenType: Type.colon},
        {tokenValue: 123, tokenType: Type.number},
        {tokenValue: ",", tokenType: Type.comma},
        {tokenValue: "}", tokenType: Type.curlyRight},
    ]
    const result = ufmi(tokenList, 0)
    log('result:', result)
    /*
    预期的结果是:
    {
        identifier: 'o',
        name: 'var',
        value: {
            name: 'object',
            objectProperties: [
                {
                    key: "value",
                    value: 123,
                },
            ]
        }
    }
    * */
}

const test = () => {
    // test1()
    // test2()
    // test3()
    test5()
    // test4()
}

const _main = () => {
    log('parser')
    test()
}

_main()



